{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": [],
      "gpuType": "T4"
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    },
    "accelerator": "GPU"
  },
  "cells": [
    {
      "cell_type": "markdown",
      "source": [
        "# Training a Simple GAN with PyTorch\n",
        "- GAN for Generating simple MNIST Images\n",
        "- Good to have a GPU for faster training"
      ],
      "metadata": {
        "id": "r2dfGLNrTmHv"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "!rm -r track\n",
        "!mkdir track"
      ],
      "metadata": {
        "id": "vT8_bZeM5ae3"
      },
      "execution_count": 1,
      "outputs": []
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {
        "id": "4YxqP7LbiUrv"
      },
      "outputs": [],
      "source": [
        "# Torch Imports\n",
        "import torch\n",
        "import torch.nn as nn\n",
        "import torch.optim as optim\n",
        "import torch.nn.functional as F\n",
        "\n",
        "# Torchvision Imports\n",
        "from torchvision import datasets, transforms\n",
        "from torchvision.utils import save_image\n",
        "\n",
        "# Device configuration; Good to have a GPU for GAN Training\n",
        "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')"
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "device"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "K5f7nyCDieqA",
        "outputId": "4d6cef5c-98af-4644-c551-0728667d6ca0"
      },
      "execution_count": 3,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "device(type='cuda')"
            ]
          },
          "metadata": {},
          "execution_count": 3
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Generator Network\n",
        "- 4 Fully Connected Layers\n",
        "- Output Dimension will be same as 28*28"
      ],
      "metadata": {
        "id": "SbdcNZOHTUgM"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class Generator(nn.Module):\n",
        "    def __init__(self, g_input_dim, g_output_dim, num_classes):\n",
        "        super(Generator, self).__init__()\n",
        "        self.fc1 = nn.Linear(g_input_dim + num_classes, 256)\n",
        "        self.fc2 = nn.Linear(256, 512)\n",
        "        self.fc3 = nn.Linear(512, 1024)\n",
        "        self.fc4 = nn.Linear(1024, g_output_dim)\n",
        "\n",
        "\n",
        "    # forward method\n",
        "    def forward(self, x, y):\n",
        "        x = torch.cat([x, y.squeeze()], 1)\n",
        "        x = F.leaky_relu(self.fc1(x), negative_slope=0.2)\n",
        "        x = F.leaky_relu(self.fc2(x), negative_slope=0.2)\n",
        "        x = F.leaky_relu(self.fc3(x), negative_slope=0.2)\n",
        "        return torch.tanh(self.fc4(x))\n"
      ],
      "metadata": {
        "id": "lYTu38GFik4Z"
      },
      "execution_count": 4,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Discriminator Network\n",
        "- 4 Fully Connected Layers\n",
        "- Output is a Single Neuron, 1=Real Image, 0=Fake Image"
      ],
      "metadata": {
        "id": "G-JIxeU0TcyI"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class Discriminator(nn.Module):\n",
        "    def __init__(self, d_input_dim, num_classes):\n",
        "        super(Discriminator, self).__init__()\n",
        "        self.fc1 = nn.Linear(d_input_dim + num_classes, 1024)\n",
        "        self.fc2 = nn.Linear(1024, 512)\n",
        "        self.fc3 = nn.Linear(512, 256)\n",
        "        self.fc4 = nn.Linear(256, 1)\n",
        "\n",
        "    # forward method\n",
        "    def forward(self, x, y):\n",
        "        x = torch.cat([x, y.squeeze()], 1)\n",
        "        x = F.leaky_relu(self.fc1(x), negative_slope=0.2)\n",
        "        x = F.dropout(x, p=0.3)\n",
        "        x = F.leaky_relu(self.fc2(x), negative_slope=0.2)\n",
        "        x = F.dropout(x, p=0.3)\n",
        "        x = F.leaky_relu(self.fc3(x), negative_slope=0.2)\n",
        "        x = F.dropout(x, p=0.3)\n",
        "        return torch.sigmoid(self.fc4(x))\n"
      ],
      "metadata": {
        "id": "Is-ZNi1tio-_"
      },
      "execution_count": 5,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Instantiating Models\n",
        "- Using a random noise of 100 dimensions\n",
        "- Using BCE Loss for Training\n",
        "- Using Adam Optimizer for Training"
      ],
      "metadata": {
        "id": "nvYTEfyMTJKV"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# Models\n",
        "z_dim = 100 # Dimension of the Input Noise to the Generator\n",
        "mnist_dim = 28 * 28 # As we have 28x28 pixel images in MNIST\n",
        "num_classes = 10\n",
        "G = Generator(g_input_dim = z_dim, g_output_dim = mnist_dim, num_classes=num_classes).to(device)\n",
        "D = Discriminator(mnist_dim,  num_classes=num_classes).to(device)\n",
        "\n",
        "# loss\n",
        "criterion = nn.BCELoss()\n",
        "\n",
        "# optimizer\n",
        "lr = 0.0002\n",
        "G_optimizer = optim.Adam(G.parameters(), lr = lr)\n",
        "D_optimizer = optim.Adam(D.parameters(), lr = lr)"
      ],
      "metadata": {
        "id": "RWhtpwPvi1Gw"
      },
      "execution_count": 6,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Loading & Normalizing MNIST Dataset"
      ],
      "metadata": {
        "id": "MQD5--pkTCwi"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# Batch Size for Training\n",
        "batch_size = 100\n",
        "\n",
        "# Transforming Images to Tensor and then Normalizing the values to be between 0 and 1, with mean 0.5 and std 0.5\n",
        "transform = transforms.Compose([\n",
        "    transforms.ToTensor(),\n",
        "    transforms.Normalize(mean=(0.5,), std=(0.5,))])\n",
        "\n",
        "# Loading the Training Dataset and applying Transformation\n",
        "train_dataset = datasets.MNIST(root='./mnist/', train=True, transform=transform, download=True)\n",
        "test_dataset = datasets.MNIST(root='./mnist/', train=False, transform=transform, download=False)\n",
        "\n",
        "# Data Loader which will be used as input to the models.\n",
        "train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)\n",
        "test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False)\n"
      ],
      "metadata": {
        "id": "Mi4nn196i4bi"
      },
      "execution_count": 7,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Method for Training Discriminator"
      ],
      "metadata": {
        "id": "uG5014WuTANh"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "def train_discriminator(mnist_data_batch, mnist_label_batch):\n",
        "    #===================Discriminator-Trainer===================#\n",
        "    D.zero_grad()\n",
        "\n",
        "    # train discriminator on real\n",
        "    x_real, y_real = mnist_data_batch.view(-1, mnist_dim), torch.ones(batch_size, 1)\n",
        "    x_real, y_real = x_real.to(device), y_real.to(device)\n",
        "    x_label = torch.nn.functional.one_hot(mnist_label_batch, num_classes=10).to(device)\n",
        "\n",
        "    # Loss for Real MNIST Data\n",
        "    D_output = D(x_real, x_label)\n",
        "    D_real_loss = criterion(D_output, y_real)\n",
        "\n",
        "    # Generate Data from Generator Network for Training\n",
        "    z = torch.randn(batch_size, z_dim).to(device) # Random Noise as Input to G\n",
        "    label = torch.randint(0, 9, (batch_size, 1)).to(device)\n",
        "    label = torch.nn.functional.one_hot(label, num_classes=10)\n",
        "    x_fake, y_fake = G(z, label), torch.zeros(batch_size, 1).to(device)\n",
        "\n",
        "    # Loss for Fake Data from Generator\n",
        "    D_output = D(x_fake, label)\n",
        "    D_fake_loss = criterion(D_output, y_fake)\n",
        "\n",
        "    # Updating only D's weights, so training on D on total loss\n",
        "    D_loss = D_real_loss + D_fake_loss\n",
        "    D_loss.backward()\n",
        "    D_optimizer.step()\n",
        "\n",
        "    return  D_loss.data.item()\n"
      ],
      "metadata": {
        "id": "RZkoCFbQi8Et"
      },
      "execution_count": 8,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Method for Training Generator"
      ],
      "metadata": {
        "id": "svYCG8BRS8Go"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "def train_generator():\n",
        "    #=======================Generator-Trainer=======================#\n",
        "    G.zero_grad()\n",
        "\n",
        "    # Generate Random Noise to use as Input to the Generator\n",
        "    z = torch.randn(batch_size, z_dim).to(device)\n",
        "    label = torch.randint(0, 9, (batch_size, 1)).to(device)\n",
        "    label = torch.nn.functional.one_hot(label, num_classes=10)\n",
        "    # The final label in this case in 1(True), i.e the Discriminator Model\n",
        "    # Thinks these are real images therefore the losses for Generator\n",
        "    # Network should update the weights of G in such a way that it produces more\n",
        "    # Real looking images.\n",
        "    y = torch.ones(batch_size, 1).to(device)\n",
        "\n",
        "    # Generate Images\n",
        "    G_output = G(z, label)\n",
        "    # Get output from Discriminator for the Generated Images\n",
        "    D_output = D(G_output, label)\n",
        "    # Compute Loss\n",
        "    G_loss = criterion(D_output, y)\n",
        "\n",
        "    # Updating only G's weights\n",
        "    G_loss.backward()\n",
        "    G_optimizer.step()\n",
        "\n",
        "    return G_loss.data.item()"
      ],
      "metadata": {
        "id": "5_ZOsm6zjAj5"
      },
      "execution_count": 9,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Main Training Loop"
      ],
      "metadata": {
        "id": "M0zmaw4MS6bP"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# Total number of epochs for Training\n",
        "n_epoch = 200\n",
        "n_critic = 3\n",
        "\n",
        "losses = []\n",
        "\n",
        "for epoch in range(n_epoch):\n",
        "    D_losses, G_losses = [], []\n",
        "    # For Every Batch in the MNIST dataset\n",
        "    # We train the Discriminator First, then the Generator\n",
        "    for batch_idx, (mnist_input_data, label) in enumerate(train_loader):\n",
        "        for _ in range(n_critic):\n",
        "          D_losses.append(train_discriminator(mnist_input_data, label))\n",
        "        G_losses.append(train_generator())\n",
        "\n",
        "    if epoch % 10 == 0:\n",
        "        test_z = torch.randn(10, z_dim).to(device)\n",
        "        label_z = torch.Tensor([0,1,2,3,4,5,6,7,8,9]).long().to(device)\n",
        "        label_z = torch.nn.functional.one_hot(label_z, num_classes=10)\n",
        "        generated = G(test_z, label_z)\n",
        "        save_image(generated.view(generated.size(0), 1, 28, 28), f'./track/sample_images_{epoch}' + '.png')\n",
        "\n",
        "    # Logging the Loss values from Discriminator and Generator\n",
        "    loss_d = torch.mean(torch.FloatTensor(D_losses))\n",
        "    loss_g = torch.mean(torch.FloatTensor(G_losses))\n",
        "    losses.append((loss_d, loss_g))\n",
        "    print(f'[{epoch}/{n_epoch}]: loss_d: {loss_d}, loss_g: {loss_g}')"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "BNEl9XfflU21",
        "outputId": "2a18477c-1d72-4124-a18a-7abeb4572477"
      },
      "execution_count": 10,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "[0/200]: loss_d: 0.10644786059856415, loss_g: 8.22718620300293\n",
            "[1/200]: loss_d: 0.020778769627213478, loss_g: 9.448699951171875\n",
            "[2/200]: loss_d: 0.036165814846754074, loss_g: 10.291396141052246\n",
            "[3/200]: loss_d: 0.05740787833929062, loss_g: 8.778903007507324\n",
            "[4/200]: loss_d: 0.07632704079151154, loss_g: 8.002867698669434\n",
            "[5/200]: loss_d: 0.10165167599916458, loss_g: 7.431113243103027\n",
            "[6/200]: loss_d: 0.12869080901145935, loss_g: 6.981318950653076\n",
            "[7/200]: loss_d: 0.1375935971736908, loss_g: 6.612727165222168\n",
            "[8/200]: loss_d: 0.19762851297855377, loss_g: 5.689815521240234\n",
            "[9/200]: loss_d: 0.24561025202274323, loss_g: 5.056235313415527\n",
            "[10/200]: loss_d: 0.29802098870277405, loss_g: 4.418086051940918\n",
            "[11/200]: loss_d: 0.35493767261505127, loss_g: 3.956519365310669\n",
            "[12/200]: loss_d: 0.4085768759250641, loss_g: 3.5331473350524902\n",
            "[13/200]: loss_d: 0.47234395146369934, loss_g: 3.157463550567627\n",
            "[14/200]: loss_d: 0.5450807213783264, loss_g: 2.810398578643799\n",
            "[15/200]: loss_d: 0.5932224988937378, loss_g: 2.613884449005127\n",
            "[16/200]: loss_d: 0.6411577463150024, loss_g: 2.414893865585327\n",
            "[17/200]: loss_d: 0.6844707131385803, loss_g: 2.2432589530944824\n",
            "[18/200]: loss_d: 0.7179568409919739, loss_g: 2.1397945880889893\n",
            "[19/200]: loss_d: 0.7595191597938538, loss_g: 1.9858351945877075\n",
            "[20/200]: loss_d: 0.7798432111740112, loss_g: 1.9473776817321777\n",
            "[21/200]: loss_d: 0.8037587404251099, loss_g: 1.8560502529144287\n",
            "[22/200]: loss_d: 0.8284605145454407, loss_g: 1.7879425287246704\n",
            "[23/200]: loss_d: 0.8394809365272522, loss_g: 1.7484127283096313\n",
            "[24/200]: loss_d: 0.8661667108535767, loss_g: 1.6769287586212158\n",
            "[25/200]: loss_d: 0.8758812546730042, loss_g: 1.6545180082321167\n",
            "[26/200]: loss_d: 0.8878630995750427, loss_g: 1.6246652603149414\n",
            "[27/200]: loss_d: 0.9009474515914917, loss_g: 1.5881613492965698\n",
            "[28/200]: loss_d: 0.9267415404319763, loss_g: 1.5172802209854126\n",
            "[29/200]: loss_d: 0.9330999851226807, loss_g: 1.5012893676757812\n",
            "[30/200]: loss_d: 0.9454710483551025, loss_g: 1.4719904661178589\n",
            "[31/200]: loss_d: 0.9469460844993591, loss_g: 1.4832251071929932\n",
            "[32/200]: loss_d: 0.9503065943717957, loss_g: 1.4640228748321533\n",
            "[33/200]: loss_d: 0.9535360932350159, loss_g: 1.466041922569275\n",
            "[34/200]: loss_d: 0.9680392742156982, loss_g: 1.425152063369751\n",
            "[35/200]: loss_d: 0.9780282378196716, loss_g: 1.4058563709259033\n",
            "[36/200]: loss_d: 0.97989821434021, loss_g: 1.396934151649475\n",
            "[37/200]: loss_d: 0.9929549694061279, loss_g: 1.375182867050171\n",
            "[38/200]: loss_d: 0.9980608224868774, loss_g: 1.357664942741394\n",
            "[39/200]: loss_d: 1.003375768661499, loss_g: 1.3438788652420044\n",
            "[40/200]: loss_d: 1.0045031309127808, loss_g: 1.3547849655151367\n",
            "[41/200]: loss_d: 1.0225317478179932, loss_g: 1.2976255416870117\n",
            "[42/200]: loss_d: 1.011902928352356, loss_g: 1.3345673084259033\n",
            "[43/200]: loss_d: 1.014864206314087, loss_g: 1.3358160257339478\n",
            "[44/200]: loss_d: 1.0210410356521606, loss_g: 1.3119503259658813\n",
            "[45/200]: loss_d: 1.0338306427001953, loss_g: 1.276951551437378\n",
            "[46/200]: loss_d: 1.027215600013733, loss_g: 1.307008981704712\n",
            "[47/200]: loss_d: 1.027419924736023, loss_g: 1.300249457359314\n",
            "[48/200]: loss_d: 1.03350830078125, loss_g: 1.2764943838119507\n",
            "[49/200]: loss_d: 1.0434062480926514, loss_g: 1.2713102102279663\n",
            "[50/200]: loss_d: 1.0425236225128174, loss_g: 1.2658803462982178\n",
            "[51/200]: loss_d: 1.0337470769882202, loss_g: 1.2911485433578491\n",
            "[52/200]: loss_d: 1.0358855724334717, loss_g: 1.2864655256271362\n",
            "[53/200]: loss_d: 1.0483258962631226, loss_g: 1.2574219703674316\n",
            "[54/200]: loss_d: 1.0453606843948364, loss_g: 1.2665221691131592\n",
            "[55/200]: loss_d: 1.061077356338501, loss_g: 1.2275958061218262\n",
            "[56/200]: loss_d: 1.051802396774292, loss_g: 1.2528489828109741\n",
            "[57/200]: loss_d: 1.0539416074752808, loss_g: 1.248396635055542\n",
            "[58/200]: loss_d: 1.050890326499939, loss_g: 1.2562931776046753\n",
            "[59/200]: loss_d: 1.057904601097107, loss_g: 1.2303454875946045\n",
            "[60/200]: loss_d: 1.0611841678619385, loss_g: 1.2288156747817993\n",
            "[61/200]: loss_d: 1.062780499458313, loss_g: 1.2278369665145874\n",
            "[62/200]: loss_d: 1.0652117729187012, loss_g: 1.2140287160873413\n",
            "[63/200]: loss_d: 1.0649200677871704, loss_g: 1.2196367979049683\n",
            "[64/200]: loss_d: 1.0647931098937988, loss_g: 1.2230851650238037\n",
            "[65/200]: loss_d: 1.0650529861450195, loss_g: 1.2267050743103027\n",
            "[66/200]: loss_d: 1.066271185874939, loss_g: 1.2182731628417969\n",
            "[67/200]: loss_d: 1.0710996389389038, loss_g: 1.2090154886245728\n",
            "[68/200]: loss_d: 1.0685330629348755, loss_g: 1.2168468236923218\n",
            "[69/200]: loss_d: 1.0655943155288696, loss_g: 1.2260276079177856\n",
            "[70/200]: loss_d: 1.0752644538879395, loss_g: 1.191250205039978\n",
            "[71/200]: loss_d: 1.0805943012237549, loss_g: 1.18320631980896\n",
            "[72/200]: loss_d: 1.0719714164733887, loss_g: 1.2091996669769287\n",
            "[73/200]: loss_d: 1.0660916566848755, loss_g: 1.226912021636963\n",
            "[74/200]: loss_d: 1.0773179531097412, loss_g: 1.192642092704773\n",
            "[75/200]: loss_d: 1.0816912651062012, loss_g: 1.187723994255066\n",
            "[76/200]: loss_d: 1.0888869762420654, loss_g: 1.1664414405822754\n",
            "[77/200]: loss_d: 1.0776625871658325, loss_g: 1.2122334241867065\n",
            "[78/200]: loss_d: 1.0725961923599243, loss_g: 1.2109605073928833\n",
            "[79/200]: loss_d: 1.0840426683425903, loss_g: 1.18372642993927\n",
            "[80/200]: loss_d: 1.0816644430160522, loss_g: 1.1898897886276245\n",
            "[81/200]: loss_d: 1.0802007913589478, loss_g: 1.1963449716567993\n",
            "[82/200]: loss_d: 1.0773208141326904, loss_g: 1.1997044086456299\n",
            "[83/200]: loss_d: 1.0840257406234741, loss_g: 1.1763547658920288\n",
            "[84/200]: loss_d: 1.0792721509933472, loss_g: 1.1930869817733765\n",
            "[85/200]: loss_d: 1.0866460800170898, loss_g: 1.16996169090271\n",
            "[86/200]: loss_d: 1.0916410684585571, loss_g: 1.1694859266281128\n",
            "[87/200]: loss_d: 1.0827382802963257, loss_g: 1.1771459579467773\n",
            "[88/200]: loss_d: 1.0810717344284058, loss_g: 1.1900677680969238\n",
            "[89/200]: loss_d: 1.0875005722045898, loss_g: 1.1801055669784546\n",
            "[90/200]: loss_d: 1.0868239402770996, loss_g: 1.1729291677474976\n",
            "[91/200]: loss_d: 1.0813649892807007, loss_g: 1.1891164779663086\n",
            "[92/200]: loss_d: 1.0753114223480225, loss_g: 1.2046868801116943\n",
            "[93/200]: loss_d: 1.0857653617858887, loss_g: 1.176581621170044\n",
            "[94/200]: loss_d: 1.0809543132781982, loss_g: 1.1937353610992432\n",
            "[95/200]: loss_d: 1.097078800201416, loss_g: 1.1502728462219238\n",
            "[96/200]: loss_d: 1.0786302089691162, loss_g: 1.2053554058074951\n",
            "[97/200]: loss_d: 1.0913784503936768, loss_g: 1.168626308441162\n",
            "[98/200]: loss_d: 1.0753552913665771, loss_g: 1.204530119895935\n",
            "[99/200]: loss_d: 1.0848866701126099, loss_g: 1.1821784973144531\n",
            "[100/200]: loss_d: 1.09109365940094, loss_g: 1.1628159284591675\n",
            "[101/200]: loss_d: 1.0912307500839233, loss_g: 1.1674017906188965\n",
            "[102/200]: loss_d: 1.0787564516067505, loss_g: 1.2046250104904175\n",
            "[103/200]: loss_d: 1.0863159894943237, loss_g: 1.1767162084579468\n",
            "[104/200]: loss_d: 1.0844924449920654, loss_g: 1.1869434118270874\n",
            "[105/200]: loss_d: 1.0883630514144897, loss_g: 1.1771891117095947\n",
            "[106/200]: loss_d: 1.0869842767715454, loss_g: 1.1811083555221558\n",
            "[107/200]: loss_d: 1.0890249013900757, loss_g: 1.1755380630493164\n",
            "[108/200]: loss_d: 1.088600516319275, loss_g: 1.1715832948684692\n",
            "[109/200]: loss_d: 1.079373836517334, loss_g: 1.1985572576522827\n",
            "[110/200]: loss_d: 1.0837699174880981, loss_g: 1.1883995532989502\n",
            "[111/200]: loss_d: 1.081546664237976, loss_g: 1.191760540008545\n",
            "[112/200]: loss_d: 1.083662748336792, loss_g: 1.1848063468933105\n",
            "[113/200]: loss_d: 1.0909007787704468, loss_g: 1.170081615447998\n",
            "[114/200]: loss_d: 1.0765677690505981, loss_g: 1.1992233991622925\n",
            "[115/200]: loss_d: 1.0869596004486084, loss_g: 1.1826567649841309\n",
            "[116/200]: loss_d: 1.081217646598816, loss_g: 1.1907680034637451\n",
            "[117/200]: loss_d: 1.0862290859222412, loss_g: 1.182869553565979\n",
            "[118/200]: loss_d: 1.072710394859314, loss_g: 1.2170894145965576\n",
            "[119/200]: loss_d: 1.0736494064331055, loss_g: 1.2186554670333862\n",
            "[120/200]: loss_d: 1.087618112564087, loss_g: 1.1770485639572144\n",
            "[121/200]: loss_d: 1.089389443397522, loss_g: 1.1714978218078613\n",
            "[122/200]: loss_d: 1.0791759490966797, loss_g: 1.2027931213378906\n",
            "[123/200]: loss_d: 1.0775606632232666, loss_g: 1.2096091508865356\n",
            "[124/200]: loss_d: 1.0791149139404297, loss_g: 1.2003000974655151\n",
            "[125/200]: loss_d: 1.0746040344238281, loss_g: 1.2144348621368408\n",
            "[126/200]: loss_d: 1.0767786502838135, loss_g: 1.2033451795578003\n",
            "[127/200]: loss_d: 1.0775060653686523, loss_g: 1.2051233053207397\n",
            "[128/200]: loss_d: 1.0759726762771606, loss_g: 1.1954110860824585\n",
            "[129/200]: loss_d: 1.0768729448318481, loss_g: 1.210829496383667\n",
            "[130/200]: loss_d: 1.0763226747512817, loss_g: 1.2051156759262085\n",
            "[131/200]: loss_d: 1.081082820892334, loss_g: 1.1947174072265625\n",
            "[132/200]: loss_d: 1.0713649988174438, loss_g: 1.2241207361221313\n",
            "[133/200]: loss_d: 1.0753154754638672, loss_g: 1.2098675966262817\n",
            "[134/200]: loss_d: 1.066478967666626, loss_g: 1.2338405847549438\n",
            "[135/200]: loss_d: 1.0688964128494263, loss_g: 1.2177525758743286\n",
            "[136/200]: loss_d: 1.0684499740600586, loss_g: 1.2172770500183105\n",
            "[137/200]: loss_d: 1.0724296569824219, loss_g: 1.2192003726959229\n",
            "[138/200]: loss_d: 1.06185781955719, loss_g: 1.2484052181243896\n",
            "[139/200]: loss_d: 1.0650769472122192, loss_g: 1.236308217048645\n",
            "[140/200]: loss_d: 1.070947289466858, loss_g: 1.221758246421814\n",
            "[141/200]: loss_d: 1.0702377557754517, loss_g: 1.2187703847885132\n",
            "[142/200]: loss_d: 1.0656274557113647, loss_g: 1.2375693321228027\n",
            "[143/200]: loss_d: 1.06340754032135, loss_g: 1.2379564046859741\n",
            "[144/200]: loss_d: 1.060541033744812, loss_g: 1.2532671689987183\n",
            "[145/200]: loss_d: 1.0636982917785645, loss_g: 1.2302125692367554\n",
            "[146/200]: loss_d: 1.064078688621521, loss_g: 1.2390176057815552\n",
            "[147/200]: loss_d: 1.0613126754760742, loss_g: 1.2490068674087524\n",
            "[148/200]: loss_d: 1.0606369972229004, loss_g: 1.2492632865905762\n",
            "[149/200]: loss_d: 1.0589103698730469, loss_g: 1.2477635145187378\n",
            "[150/200]: loss_d: 1.0629887580871582, loss_g: 1.233683466911316\n",
            "[151/200]: loss_d: 1.0641170740127563, loss_g: 1.2388982772827148\n",
            "[152/200]: loss_d: 1.061639428138733, loss_g: 1.234470009803772\n",
            "[153/200]: loss_d: 1.0615154504776, loss_g: 1.2413920164108276\n",
            "[154/200]: loss_d: 1.057737112045288, loss_g: 1.2460600137710571\n",
            "[155/200]: loss_d: 1.0598456859588623, loss_g: 1.2396680116653442\n",
            "[156/200]: loss_d: 1.0539195537567139, loss_g: 1.2661187648773193\n",
            "[157/200]: loss_d: 1.0577222108840942, loss_g: 1.2440059185028076\n",
            "[158/200]: loss_d: 1.0604166984558105, loss_g: 1.2441140413284302\n",
            "[159/200]: loss_d: 1.052748680114746, loss_g: 1.2649250030517578\n",
            "[160/200]: loss_d: 1.0545297861099243, loss_g: 1.2643548250198364\n",
            "[161/200]: loss_d: 1.0539896488189697, loss_g: 1.258792757987976\n",
            "[162/200]: loss_d: 1.0626307725906372, loss_g: 1.2360143661499023\n",
            "[163/200]: loss_d: 1.0505582094192505, loss_g: 1.2742666006088257\n",
            "[164/200]: loss_d: 1.0566703081130981, loss_g: 1.2515215873718262\n",
            "[165/200]: loss_d: 1.0565900802612305, loss_g: 1.2506110668182373\n",
            "[166/200]: loss_d: 1.0475009679794312, loss_g: 1.278200387954712\n",
            "[167/200]: loss_d: 1.0496989488601685, loss_g: 1.275726318359375\n",
            "[168/200]: loss_d: 1.0508275032043457, loss_g: 1.2724804878234863\n",
            "[169/200]: loss_d: 1.0493797063827515, loss_g: 1.267189860343933\n",
            "[170/200]: loss_d: 1.0501537322998047, loss_g: 1.2659059762954712\n",
            "[171/200]: loss_d: 1.048188328742981, loss_g: 1.2712215185165405\n",
            "[172/200]: loss_d: 1.051972508430481, loss_g: 1.2619723081588745\n",
            "[173/200]: loss_d: 1.0490995645523071, loss_g: 1.27713143825531\n",
            "[174/200]: loss_d: 1.0449557304382324, loss_g: 1.2833480834960938\n",
            "[175/200]: loss_d: 1.0497767925262451, loss_g: 1.2710959911346436\n",
            "[176/200]: loss_d: 1.0516560077667236, loss_g: 1.27605402469635\n",
            "[177/200]: loss_d: 1.0470752716064453, loss_g: 1.2805906534194946\n",
            "[178/200]: loss_d: 1.0380325317382812, loss_g: 1.3112953901290894\n",
            "[179/200]: loss_d: 1.0450716018676758, loss_g: 1.2793484926223755\n",
            "[180/200]: loss_d: 1.0424493551254272, loss_g: 1.2934468984603882\n",
            "[181/200]: loss_d: 1.0460461378097534, loss_g: 1.269071102142334\n",
            "[182/200]: loss_d: 1.0495364665985107, loss_g: 1.2686864137649536\n",
            "[183/200]: loss_d: 1.0391695499420166, loss_g: 1.3039743900299072\n",
            "[184/200]: loss_d: 1.0481443405151367, loss_g: 1.2690114974975586\n",
            "[185/200]: loss_d: 1.0440771579742432, loss_g: 1.2766956090927124\n",
            "[186/200]: loss_d: 1.0394868850708008, loss_g: 1.3004838228225708\n",
            "[187/200]: loss_d: 1.043576717376709, loss_g: 1.2916706800460815\n",
            "[188/200]: loss_d: 1.0433828830718994, loss_g: 1.2846965789794922\n",
            "[189/200]: loss_d: 1.0337543487548828, loss_g: 1.3191578388214111\n",
            "[190/200]: loss_d: 1.0412585735321045, loss_g: 1.2900166511535645\n",
            "[191/200]: loss_d: 1.0356502532958984, loss_g: 1.3135936260223389\n",
            "[192/200]: loss_d: 1.0405683517456055, loss_g: 1.2843198776245117\n",
            "[193/200]: loss_d: 1.0342071056365967, loss_g: 1.3212080001831055\n",
            "[194/200]: loss_d: 1.0359978675842285, loss_g: 1.3060237169265747\n",
            "[195/200]: loss_d: 1.0438436269760132, loss_g: 1.2887029647827148\n",
            "[196/200]: loss_d: 1.035378098487854, loss_g: 1.3115779161453247\n",
            "[197/200]: loss_d: 1.0370285511016846, loss_g: 1.3036816120147705\n",
            "[198/200]: loss_d: 1.039557695388794, loss_g: 1.302491307258606\n",
            "[199/200]: loss_d: 1.0330076217651367, loss_g: 1.3067930936813354\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Generating Images from our Trained Generator Network"
      ],
      "metadata": {
        "id": "-7judlTOS247"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "num_classes"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "wkeTBmsMa4R9",
        "outputId": "9fa7bbc1-e976-4a6f-844e-ca4b49828f68"
      },
      "execution_count": 11,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "10"
            ]
          },
          "metadata": {},
          "execution_count": 11
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "with torch.no_grad():\n",
        "    test_z = torch.randn(8, z_dim).to(device)\n",
        "    label_z = torch.Tensor([6, 6, 6, 6, 6, 6, 6, 6]).long().to(device)\n",
        "    label_z = torch.nn.functional.one_hot(label_z, num_classes=10)\n",
        "    generated = G(test_z, label_z)\n",
        "\n",
        "    save_image(generated.view(generated.size(0), 1, 28, 28), './sample_images_200' + '.png')"
      ],
      "metadata": {
        "id": "gxVOotx_oxeS"
      },
      "execution_count": 12,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "import matplotlib.pyplot as plt"
      ],
      "metadata": {
        "id": "fbhmXPVx-vgR"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "plt.imshow(generated.view(generated.size(0), 1, 28, 28).cpu()[1].permute(1,2,0))"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 448
        },
        "id": "WpZVI2Snuihl",
        "outputId": "f221c3ef-f1a7-4eba-e4d1-11eca845f8a8"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "<matplotlib.image.AxesImage at 0x7f5d605d4520>"
            ]
          },
          "metadata": {},
          "execution_count": 28
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ],
            "image/png": "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C2OOj3A4rB07dlz0x8fBgwd17NixtDo+nHNavny5Nm7cqK1bt6q0tDTm9ZkzZyorKyvmeGhubtaBAwfS6nj4tP3Qn927d0tSah0P1ndBfBYvv/yyCwaDbv369e7Xv/61u/fee92oUaNce3u7dWuD6tvf/rarr693ra2t7pe//KWrqKhw+fn57siRI9atJVVXV5fbtWuX27Vrl5PknnzySbdr1y734YcfOuec++EPf+hGjRrlNm/e7Pbs2eMWLFjgSktL3UcffWTceWKdbz90dXW5Bx980DU2NrrW1lb3zjvvuC9+8YvuqquucqdOnbJuPWGWLVvmQqGQq6+vd4cPH44uJ0+ejK5z3333ufHjx7utW7e6nTt3uvLycldeXm7YdeJ92n5oaWlx3/ve99zOnTtda2ur27x5s5s4caKbPXu2ceexhkQAOefcM88848aPH++ys7PdrFmz3Pbt261bGnS33XabKyoqctnZ2W7s2LHutttucy0tLdZtJd27777rJJ2zLF682Dl39lbsRx991BUUFLhgMOjmzp3rmpubbZtOgvPth5MnT7p58+a50aNHu6ysLDdhwgS3dOnStPshrb9/vyS3bt266DofffSR++Y3v+kuu+wyN3LkSHfLLbe4w4cP2zWdBJ+2Hw4cOOBmz57t8vLyXDAYdFdeeaX7zne+4zo7O20b/wTejgEAYCLlrwEBANITAQQAMEEAAQBMEEAAABMEEADABAEEADBBAAEATBBAAAATBBAAwAQBBAAwQQABAEwQQAAAE/8f0exWNHvz5DkAAAAASUVORK5CYII=\n"
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Plotting Losses from the Training"
      ],
      "metadata": {
        "id": "OEPmb8tlSyU2"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "import matplotlib.pyplot as plt\n",
        "plt.plot(range(1, len(losses)+1), [_[0] for _ in losses], label='discriminator_loss')\n",
        "plt.plot(range(1, len(losses)+1), [_[1] for _ in losses], label='generator_loss')\n",
        "plt.legend()\n",
        "plt.show()"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 450
        },
        "id": "ylatKU9TSI1s",
        "outputId": "2033e275-37f3-41ec-c4ce-ba0451822370"
      },
      "execution_count": 14,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ],
            "image/png": "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\n"
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [],
      "metadata": {
        "id": "oICcnSSvSPUD"
      },
      "execution_count": null,
      "outputs": []
    }
  ]
}